package dustdispatcher;

import java.util.ArrayList;
import java.util.List;

/**
 * This module will control the number of tasks that Dust is executing at a
 * time in order to prevent exhaust the system resources (CPU mainly).
 * 
 * If you plan to add functionality to Dust through new Threads you should
 * extend DustTask and ask this module to execute them. It will automatically
 * execute your task when possible.
 * 
 * @author ole
 */
public class DustDispatcher {
	// Class constants.
	private static final int MAX_TASKS = 5;
	
	// Class attributes.
	private static List<DispatchableTask> queued_tasks = null;
	private static List<DispatchableTask> running_tasks = null;
	
	/*
	 * Private methods.
	 */
	
	/**
	 * Ask the module to run the next task to be executed. To do so it must
	 * occur that there are tasks waiting to being executed and that we
	 * haven't reach the limit of tasks executing.
	 */
	private static synchronized void runNextTask() {
		DispatchableTask next_task;
		
		// Check the conditions needed to run a task.
		if (running_tasks.size() < MAX_TASKS &&
			queued_tasks.size() > 0) {
			// Remove the next task from the queued_tasks.
			next_task = queued_tasks.remove(0);
			
			// Add it to the queue of tasks running and run it.
			running_tasks.add(next_task);
			next_task.start();
		}
	}
	
	/*
	 * Public methods.
	 */
	
	/**
	 * Initializes the module. It checks if the module is already initialized
	 * and warn the user but will continue.
	 */
	public DustDispatcher() {
		// Check if the module is already initialized, we will continue
		// anyway but warn it to the user.
		if (queued_tasks != null ||
			running_tasks != null)
			System.err.println("[WARN]: Dispatcher module already " +
							   "initialized");
		
		queued_tasks = new ArrayList<DispatchableTask>();
		running_tasks = new ArrayList<DispatchableTask>();
	}
	
	/**
	 * Ask the module to execute a task when possible.
	 * 
	 * @param task:
	 * 		The task to be executed.
	 */
	public static synchronized void dispatchTask(DispatchableTask task) {		
		// If we have too many tasks running put this new task waiting its
		// turn.
		if (running_tasks.size() >= MAX_TASKS) {
			queued_tasks.add(task);
			return;
		}
		
		// If we can execute this task right now do it, but add it a callback
		// to signaling when it finish in order to remove it from the running
		// queue and execute another task if there are tasks waiting.
		running_tasks.add(task);
		task.start();
	}
	
	/**
	 * Removes the task from the running tasks. It checks that the task is in
	 * the queue of running tasks, if not it will continue but will warn the
	 * user.
	 * 
	 * If there are tasks waiting it will start another one.
	 * 
	 * @param task:
	 * 		The task to remove.
	 */
	public static synchronized void removeTask(DispatchableTask task) {
		/** TODO: Right now each task that search/download an image (and
		 * likely others too) sends this message. That should not happen. **/
		// Check if the task is in the running queue.
		if (!running_tasks.contains(task)) {
			System.err.println("[WARN]: Requested to remove a task that " +
							   "doesn't exists in the queue.");
			return;
		}
		
		// Remove the task from the running queue and start another one if
		// necessary.
		running_tasks.remove(task);
		DustDispatcher.runNextTask();
	}
}
